package fun.ticsmyc.rpc.common.serializer.impl;

import fun.ticsmyc.rpc.common.serializer.Serializer;
import fun.ticsmyc.rpc.common.serializer.SerializerCode;
import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Protobuf序列化器
 *   可以自己编写proto文件 ，使用官方提供的代码生成器
 *   也可以引入protobuf-runtime，在运行时生成schema，并使用一个并发Map保存
 *   【性能高 且跨语言】
 * @author Ticsmyc
 * @date 2020-10-28 16:40
 */
public class ProtobufSerializer implements Serializer {

    public final byte ID = SerializerCode.PROTOBUF.getCode();

    private static LinkedBuffer buffer = null;
    private static Map<Class<?>, Schema<?>> schemaCacheMap = null;
    private volatile static ProtobufSerializer INSTANCE = null;


    //单例懒加载， 如果不使用这个序列化器， 就不需要创建buffer和HashMap
    private ProtobufSerializer(){}
    public static ProtobufSerializer getInstance(){
        if(INSTANCE == null){
            synchronized (ProtostuffIOUtil.class){
                if(INSTANCE == null){
                    INSTANCE = new ProtobufSerializer();
                    buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
                    schemaCacheMap = new ConcurrentHashMap<>();
                }
            }
        }
        return INSTANCE;
    }

    @Override
    public byte[] serialize(Object obj) {
        Class clazz = obj.getClass();
        Schema schema = getSchema(clazz);
        byte[] data;
        try{
            data = ProtostuffIOUtil.toByteArray(obj,schema,buffer);
        }finally{
            buffer.clear();
        }
        return data;
    }

    @Override
    public Object deserialize(byte[] bytes, Class<?> clazz) {
        Schema schema = getSchema(clazz);
        Object obj = schema.newMessage();
        ProtostuffIOUtil.mergeFrom(bytes,obj,schema);
        return obj;
    }

    @Override
    public byte getId() {
        return ID;
    }

    private static Schema getSchema(Class<?> clazz){
        Schema<?> schema = schemaCacheMap.get(clazz);
        if(Objects.isNull(schema)){
            synchronized (ProtobufSerializer.class){
                if(Objects.isNull(schemaCacheMap.get(clazz))){
                    //如果没有创建过这个类的shcema， 就是用RuntimeSchema创建。
                    schema = RuntimeSchema.getSchema(clazz);
                    if(Objects.nonNull(schema)){
                        schemaCacheMap.put(clazz,schema);
                    }
                }
            }
        }
        return schema;
    }
}
